package com.zoe.onelink.logging.trace.sql.druid;

import cn.hutool.core.util.NumberUtil;
import com.alibaba.druid.DbType;
import com.alibaba.druid.filter.FilterChain;
import com.alibaba.druid.filter.FilterEventAdapter;
import com.alibaba.druid.proxy.jdbc.JdbcParameter;
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
import com.alibaba.druid.proxy.jdbc.StatementProxy;
import com.alibaba.druid.sql.SQLUtils;
import com.alibaba.fastjson.JSON;
import com.zoe.onelink.common.constant.TraceSpanConstant;
import com.zoe.onelink.logging.trace.property.TraceLogProperties;
import com.zoe.onelink.logging.trace.sql.SqlExecuteContextHolder;
import lombok.RequiredArgsConstructor;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

/**
 * <p>标题: </p>
 * <p>描述: </p>
 * <p>版权: Copyright (c) 2022</p>
 * <p>公司: 智业软件股份有限公司</p>
 *
 * @version: 1.0
 * @author: ruansheng
 * @date: 2022-07-27
 */
@RequiredArgsConstructor
public class OnelinkDruidSqlEventFilter extends FilterEventAdapter {

    private final TraceLogProperties.SqlLogProperties sqlLogProperties;
    private SQLUtils.FormatOption statementSqlFormatOption = new SQLUtils.FormatOption(false, true);

    @Override
    protected void statementExecuteBefore(StatementProxy statement, String sql) {
        statement.setLastExecuteStartNano();
        try {
            this.setSqlLogContext(statement, sql);
        } catch (Exception e) {
            // ignore
        }

    }

    @Override
    protected void statementExecuteAfter(StatementProxy statement, String sql, boolean result) {
        long nanos = System.nanoTime() - statement.getLastExecuteStartNano();
        statement.setLastExecuteTimeNano(nanos);
    }

    @Override
    public void resultSet_close(FilterChain chain, ResultSetProxy resultSet) throws SQLException {
        super.resultSet_close(chain, resultSet);
        long lastExecuteTimeNano = resultSet.getStatementProxy().getLastExecuteTimeNano();
        if (lastExecuteTimeNano <= 0) {
            return;
        }
        String consume = NumberUtil.decimalFormat("0.000", (lastExecuteTimeNano) / 1_000_000f);
        SqlExecuteContextHolder.set(SqlExecuteContextHolder.CONSUME, consume);
    }

    /**
     * 设置SQL语法上下文
     */
    private void setSqlLogContext(StatementProxy statement, String preparedSql) {
        int parametersSize = statement.getParametersSize();
        // 语法参数为空直接记录原始语法即可
        if (parametersSize == 0) {
            SqlExecuteContextHolder.set(SqlExecuteContextHolder.SQL, preparedSql);
            return;
        }
        // 语法参数列表
        List<Object> parameters = new ArrayList<>(parametersSize);
        for (int i = 0; i < parametersSize; ++i) {
            JdbcParameter jdbcParam = statement.getParameter(i);
            Object param = jdbcParam != null ? jdbcParam.getValue() : null;
            parameters.add(param);
        }
        // 语法记录策略
        switch (this.sqlLogProperties.getStrategy()) {
            // 可直接执行的语法
            case TraceLogProperties.SqlLogProperties.Strategy.FILLED:
                DbType dbType = DbType.of(statement.getConnectionProxy().getDirectDataSource().getDbType());
                String formattedSql = SQLUtils.format(preparedSql, dbType, parameters, this.statementSqlFormatOption);
                SqlExecuteContextHolder.set(SqlExecuteContextHolder.SQL, formattedSql);
                break;
            // 预编译语法和参数单独记录
            case TraceLogProperties.SqlLogProperties.Strategy.PREPARED:
                SqlExecuteContextHolder.set(SqlExecuteContextHolder.SQL, preparedSql);
                SqlExecuteContextHolder.set(SqlExecuteContextHolder.PARAM, JSON.toJSONString(parameters));
                break;
            // 只记录预编译语法
            case TraceLogProperties.SqlLogProperties.Strategy.PREPARED_ONLY:
                SqlExecuteContextHolder.set(SqlExecuteContextHolder.SQL, preparedSql);
                break;
            default:
                break;
        }


    }

}
